<?php

class TemplateParser
{

  protected $tag;

  protected $params;

  protected $content;

  protected $children = array();

  protected $search = array();

  protected $parent;

  public function __construct($params = null, $content = null, $parent = null)
  {
    $this->params = $params;
    $this->parent = $parent;
    $this->content = $content;

    $this->parseTags();
  }

  public function children($tag = null)
  {
    return isset($this->children[$tag]) ? $this->children[$tag] : array();
  }

  public function params($name = null)
  {
    if (! empty($name)) {
      return isset($this->params[$name]) ? $this->params[$name] : null;
    }

    return $this->params;
  }

  public function parent($tag = null)
  {
    if (! empty($tag) && $this->parent && $this->parent()->tag() == $tag) {
      return $this->parent;
    }

    return $this->parent ? $this->parent->parent($tag) : null;
  }

  public function __toString()
  {
    return $this->render();
  }

  protected function render()
  {
    $output = $this->content;

    foreach ($this->search as $i => $j) {
      foreach ($j as $k => $search) {
        $element = $this->children[$i][$k];
        $replace = ($element instanceof TemplateParser) ? $element->render() : $element;
        $output = str_replace($search, $replace, $output);
      }
    }

    return $this->parseVars($output);
  }

  public static function parseFile($file)
  {
    $tpl = file_get_contents($file);

    $parser = new self(null, $tpl);

    return $parser->render();
  }

  protected function parseTags()
  {
    $parsed = array();

    $pattern = '/<sy:([a-zA-Z]+)\s*((?:[^\s>="\']+=("|\')[^\3]+?\3)*)?(?:>(.*?)<\/sy:\1>|\/>)/mus';

    if (preg_match_all($pattern, $this->content, $matches, PREG_SET_ORDER)) {
      foreach($matches as $match) {
        preg_match_all('/\s*([^\s>="\']+)=("|\')(.+?)(?<!\\\)\2/mu', $match[2], $attrs, PREG_SET_ORDER);

        $params = array();
        foreach ($attrs as $attr) {
          $params[$attr[1]] = $attr[3];
        }

        $search = $match[0];
        $tag = $match[1];
        $content = sy_get_param($match, 4);

        $element = $this->factoryElement($tag, $params, $content);

        $id = md5(uniqid());

        $this->content = str_replace($search, "<!-- $id -->", $this->content);

        $this->search[$tag][] = "<!-- $id -->";
        $this->children[$tag][] = $element;
      }
    }
  }

  protected function parseVars($content, $echo = true)
  {
    $pattern = '/({[a-z_][a-z\._|0-9:]*})/i';

    if (preg_match_all($pattern, $content, $matches, PREG_SET_ORDER)) {
      foreach ($matches as $match) {
        $var = $this->parseVar($match[1]);
        if ($echo) {
          $var = '<?php echo ' . $var . '; ?>';
        }

        $content = str_replace($match[0], $var, $content);
      }
    }

    return $content;
  }

  protected function factoryElement($tag, $params, $content)
  {
    $element = $content;

    $filename = Inflector::underscore('template_' . $tag);

    $class = Inflector::camelize($filename);

    if (! class_exists($class)) {
      $file = dirname(__file__) . '/elements/' . $filename . '.php';

      if (file_exists($file)) {
        require_once($file);
      }
      else {
        $class = 'TemplateParser';
      }
    }

    $element = new $class($params, $content, $this);
    $element->tag = $tag;

    return $element;
  }

  protected static function parseVar($var)
  {
    if (preg_match('/^{([^|]*)(?:\|(.*))?}$/', $var, $o)) {
      $names = explode('.', $o[1]);

      $var = '$' . array_shift($names);

      foreach ($names as $name) {
        $var .= '[\''.$name.'\']';
      }

      if (isset($o[2])) {
        $helpers = preg_split('/\|/', $o[2], -1, PREG_SPLIT_NO_EMPTY);

        foreach ($helpers as $helper) {
          $params = explode(':', $helper);
          $name = array_shift($params);

          $_params = array();
          foreach ($params as $param) {
            $_params[] = is_numeric($param) ? $param : "'{$param}'";
          }

          if (! empty($_params)) {
            $params = implode(', ', $_params);
            $var = "View_Helper::getInstance('{$name}', \$this)->execute($var, $params)";
          }
          else {
            $var = "View_Helper::getInstance('{$name}', \$this)->execute($var)";
          }

        }
      }
    }

    return $var;
  }

}
